package com.pengju.nav_compiler


import com.google.auto.service.AutoService
import com.google.gson.Gson
import com.google.gson.JsonObject
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Filer
import javax.annotation.processing.Messager
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.annotation.processing.SupportedAnnotationTypes
import javax.annotation.processing.SupportedSourceVersion
import javax.lang.model.SourceVersion
import javax.lang.model.element.TypeElement
import com.pengju.nav_annotation.Destination
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStreamWriter
import javax.lang.model.element.Element
import javax.lang.model.type.DeclaredType
import javax.tools.Diagnostic
import javax.tools.StandardLocation
import kotlin.math.abs
import javax.annotation.processing.Processor


@AutoService(Processor::class)
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@SupportedAnnotationTypes("com.pengju.nav_annotation.Destination")class NavProcessor : AbstractProcessor() {
    companion object {
        private const val PAGE_TYPE_ACTIVITY = "Activity"
        private const val PAGE_TYPE_FRAGMENT = "FRAGMENT"
        private const val PAGE_TYPE_DIALOG = "DIALOG"
        private const val DESTINATION_OUTPUT_FILE_NAME = "destination.json"
    }
    private lateinit var messager: Messager
    private lateinit var filer: Filer
    private val gson = Gson()
    override fun init(processingEnv: ProcessingEnvironment) {
        super.init(processingEnv)
        // 处理器初始化时被调用
        messager = processingEnv.messager
        messager.printMessage(Diagnostic.Kind.NOTE, "NavProcessor: init......")
        filer = processingEnv.filer

    }

    override fun process(
        /*
        Element：
        Element 是 Java 编译时注解处理中的一个接口，表示程序中的元素，如类、方法、字段、参数等。
        Element 的常见子接口包括 TypeElement、ExecutableElement、VariableElement 等。
        TypeElement：
        TypeElement 是 Element 的一个子接口，表示一个类或接口。
        通过 TypeElement，你可以获取类或接口的名称、修饰符、超类、实现的接口等信息。
        其他 Element 子接口：
        ExecutableElement：表示方法或构造方法。
        VariableElement：表示字段或方法参数。
        PackageElement：表示包。
        通过这种方式，你可以在编译阶段使用 Element 和其子接口来获取和处理类、方法、字段等信息
         */
        set: Set<TypeElement?>,
        roundEnvironment: RoundEnvironment
    ): Boolean {
        println("NavProcessor: process!")
        // 处理自定义注解
        val elements: Set<out Element> = roundEnvironment.getElementsAnnotatedWith(Destination::class.java)
        if (elements.isNotEmpty()) {
            val destinationMap: HashMap<String, JsonObject> = HashMap()
            handleDestination(elements, Destination::class.java, destinationMap)
            messager.printMessage(Diagnostic.Kind.NOTE, "handleDestination over!")
            try {
                // 创建资源文件，参数包括输出位置、输出包名和输出文件名
                val res = filer.createResource(
                    StandardLocation.CLASS_OUTPUT, // 输出位置
                    "", // 输出包名
                    DESTINATION_OUTPUT_FILE_NAME, // 输出文件名
                )

                // 获取资源文件的路径
                val resPath = res.toUri().path

                // 构建应用程序路径和资源文件路径
                val appPath = resPath.substring(0, resPath.indexOf("app") + 4)
                val assertPath = appPath + "src/main/assets"
                messager.printMessage(Diagnostic.Kind.NOTE, "Generated assertPath: $assertPath")

                // 检查并创建资源目录
                val outputDir = File(assertPath)
                if (!outputDir.exists()) {
                    outputDir.mkdirs()
                }
                // 创建资源文件
                outputDir.createNewFile()

                // 将destinationMap内容转换为JSON字符串
                val content = gson.toJson(destinationMap)


                // 检查并删除已存在的输出文件
                val outputFile = File(assertPath, DESTINATION_OUTPUT_FILE_NAME)
                if (outputFile.exists()) {
                    outputFile.delete()
                }

                // 重新创建输出文件并写入内容
                outputFile.createNewFile()
                val fileOutputSteam = FileOutputStream(outputFile)
                val outputStreamWriter = OutputStreamWriter(fileOutputSteam)
                outputStreamWriter.write(content)
                outputStreamWriter.flush()

                // 关闭文件流
                fileOutputSteam.close()
                outputStreamWriter.close()

                // 打印成功消息
                messager.printMessage(
                    Diagnostic.Kind.NOTE,
                    "==================> destination.json create success <=================="
                )
            } catch (e: Exception) {
                // 打印错误消息并输出异常堆栈
                messager.printMessage(
                    Diagnostic.Kind.ERROR,
                    "==================> destination.json create fail <=================="
                )
                e.printStackTrace()
            }

        }
        return false
    }

    // 处理注解
    private fun handleDestination(
        elements: Set<out Element>,
        destinationClass: Class<Destination>,
        destinationMap: HashMap<String, JsonObject>
    ) {
        elements.map {element ->
            element as TypeElement
        }.forEach {typeElement ->
            val clazzName = typeElement.qualifiedName.toString()
            val annotation = typeElement.getAnnotation(destinationClass)
            val tag = annotation.tag
            val pageUrl = annotation.pageUrl
            val asStarter = annotation.asStarter
            val id = abs(clazzName.hashCode())
            // 得到 Activity / Fragment / Dialog
            val destType = getDestinationType(typeElement)
            if (destinationMap.containsKey(pageUrl)) {
                messager.printMessage(Diagnostic.Kind.ERROR, "不同页面禁止使用相同pageUrl: $pageUrl")
            }else {
                val jsonObject = JsonObject()
                jsonObject.addProperty("tag", tag)
                jsonObject.addProperty("clazzName", clazzName)
                jsonObject.addProperty("pageUrl", pageUrl)
                jsonObject.addProperty("asStarter", asStarter)
                jsonObject.addProperty("pageID", id)
                jsonObject.addProperty("destType", destType)
                destinationMap[tag] = jsonObject
            }
        }
    }

    private fun getDestinationType(typeElement: TypeElement): String? {
        val typeMirror = typeElement.superclass
        val superClazzName = typeMirror.toString()
        if (superClazzName.contains(PAGE_TYPE_ACTIVITY.lowercase())){
            return PAGE_TYPE_ACTIVITY.lowercase()
        }
        if (superClazzName.contains(PAGE_TYPE_FRAGMENT.lowercase())){
            return PAGE_TYPE_FRAGMENT.lowercase()
        }
        if (superClazzName.contains(PAGE_TYPE_DIALOG.lowercase())){
            return PAGE_TYPE_DIALOG.lowercase()
        }
        // 检查给定的 typeMirror 是否为 DeclaredType 类型，
        // 并进一步检查其对应的元素是否为 TypeElement 类型。
        // 如果是，则调用 getDestinationType 方法处理该元素。
        if (typeMirror is DeclaredType) {
            val asElement = typeMirror.asElement()
            if (asElement is TypeElement) {
                getDestinationType(asElement)
            }
        }
        return null
    }
}